package com.fight.strive.sys.modules.rbac.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fight.strive.sys.modules.rbac.constants.RbacAuthConstants;
import com.fight.strive.sys.modules.rbac.dao.RbacRoleAuthMapper;
import com.fight.strive.sys.modules.rbac.dto.RbacRoleAuthDto;
import com.fight.strive.sys.modules.rbac.entity.RbacAuthEntity;
import com.fight.strive.sys.modules.rbac.entity.RbacRoleAuthEntity;
import com.fight.strive.sys.modules.rbac.entity.RbacRoleEntity;
import com.fight.strive.sys.modules.rbac.service.RbacAuthService;
import com.fight.strive.sys.modules.rbac.service.RbacRoleAuthService;
import com.fight.strive.sys.modules.rbac.service.RbacRoleService;
import com.fight.strive.sys.utils.CollectionUtils;
import com.fight.strive.sys.utils.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

/**
 * @author ZHOUXIANG
 */
@Service
@Slf4j
public class RbacRoleAuthServiceImpl
        extends ServiceImpl<RbacRoleAuthMapper, RbacRoleAuthEntity>
        implements RbacRoleAuthService {

    @Resource
    @Lazy
    private RbacAuthService rbacAuthService;

    @Resource
    private RbacRoleService rbacRoleService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(@Valid RbacRoleAuthDto dto) {
        // 删除现在分配的
        this.delete(dto);
        // 权限再分配
        if (CollectionUtils.isNotEmpty(dto.getAuthIds())) {
            RbacRoleEntity roleEntity = rbacRoleService.getById(dto.getRoleId());
            if (ObjectUtils.notNull(roleEntity)) {
                List<RbacRoleAuthEntity> list = new ArrayList<>();
                dto.getAuthIds().forEach(authId -> {
                    RbacAuthEntity authEntity = rbacAuthService.getById(authId);
                    if (ObjectUtils.notNull(authEntity)) {
                        RbacRoleAuthEntity rbacRoleAuthEntity = new RbacRoleAuthEntity();
                        rbacRoleAuthEntity.setRoleId(roleEntity.getId())
                                .setRoleCode(roleEntity.getRoleCode())
                                .setRoleName(roleEntity.getRoleName())
                                .setAuthId(authEntity.getId())
                                .setAuthCode(authEntity.getAuthCode())
                                .setAuthName(authEntity.getAuthName());
                        this.saveOrUpdate(rbacRoleAuthEntity);
                        list.add(rbacRoleAuthEntity);
                    }
                });
                this.updateRedisData(roleEntity.getId(), list);
            }
        }
    }

    @Override
    public void delete(@Valid RbacRoleAuthDto dto) {
        // 删除缓存
        this.deleteRedisData(dto.getRoleId());
        // 删除数据库
        QueryWrapper<RbacRoleAuthEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("role_id", dto.getRoleId());
        this.remove(queryWrapper);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Long> getAuthIds(Long roleId) {
        List<Long> authIds = (List<Long>) redisTemplate.opsForHash().get(
                RbacAuthConstants.ROLE_AUTH_IDS_KEY, roleId);
        if (CollectionUtils.isEmpty(authIds)) {
            authIds = new ArrayList<>();
            List<RbacRoleAuthEntity> entities = this.getRoleAuthByRoleId(roleId);
            if (CollectionUtils.isNotEmpty(entities)) {
                for (RbacRoleAuthEntity entity : entities) {
                    authIds.add(entity.getAuthId());
                }
            }
            // 写入缓存，空列表也缓存
            redisTemplate.opsForHash().put(
                    RbacAuthConstants.ROLE_AUTH_IDS_KEY, roleId, authIds);
        }
        return authIds;
    }

    @Override
    public HashSet<String> getUserRoleAuth(HashSet<String> roleSet) {
        HashSet<String> authSet = new HashSet<>();
        if (CollectionUtils.isNotEmpty(roleSet)) {
            // this.getRoleAuth 会从缓存中读取角色代码对应的权限
            roleSet.forEach(rs ->
                    authSet.addAll(this.getRoleAuth(rs)));
        }
        return authSet;
    }

    @Override
    public HashSet<String> getRoleAuth(String roleCode) {
        HashSet<String> authSet = new HashSet<>();
        // 有缓存，就从缓存中取出 role entity
        RbacRoleEntity roleEntity = rbacRoleService.getRoleByCode(roleCode);
        if (ObjectUtils.notNull(roleEntity)) {
            // 有缓存就从缓存中取出 roleId 对应的权限列表
            List<RbacRoleAuthEntity> list =
                    this.getRoleAuthByRoleId(roleEntity.getId());
            if (CollectionUtils.isNotEmpty(list)) {
                list.forEach(ra -> authSet.add(ra.getAuthCode()));
            }
        }
        return authSet;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<RbacRoleAuthEntity> getRoleAuthByRoleId(Long roleId) {
        List<RbacRoleAuthEntity> list =
                (List<RbacRoleAuthEntity>) redisTemplate.opsForHash().get(
                        RbacAuthConstants.ROLE_AUTH_LIST_KEY, roleId);
        if (CollectionUtils.isEmpty(list)) {
            QueryWrapper<RbacRoleAuthEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("role_id", roleId);
            list = this.list(queryWrapper);
            // list为空时也进行缓存
            this.updateRedisData(roleId, list);
        }
        return list;
    }

    @Override
    public List<RbacRoleAuthEntity> getRoleAuthByRoleCode(String roleCode) {
        // 有缓存从缓存取出
        RbacRoleEntity roleEntity = rbacRoleService.getRoleByCode(roleCode);
        if (ObjectUtils.notNull(roleEntity)) {
            // 有缓存就从缓存中取出
            return this.getRoleAuthByRoleId(roleEntity.getId());
        }
        return Collections.emptyList();
    }

    @Override
    public void updateRedisData(Long roleId, List<RbacRoleAuthEntity> auths) {
        redisTemplate.opsForHash().put(
                RbacAuthConstants.ROLE_AUTH_LIST_KEY, roleId, auths);
        HashSet<String> authCodes = new HashSet<>();
        List<Long> ids = new ArrayList<>();
        for (RbacRoleAuthEntity entity : auths) {
            authCodes.add(entity.getAuthCode());
            ids.add(entity.getAuthId());
        }
        redisTemplate.opsForHash().put(
                RbacAuthConstants.ROLE_AUTH_CODES_KEY, roleId, authCodes);
        redisTemplate.opsForHash().put(
                RbacAuthConstants.ROLE_AUTH_IDS_KEY, roleId, ids);
    }

    @Override
    public void deleteRedisData(Long roleId) {
        redisTemplate.opsForHash().delete(
                RbacAuthConstants.ROLE_AUTH_LIST_KEY, roleId);
        redisTemplate.opsForHash().delete(
                RbacAuthConstants.ROLE_AUTH_CODES_KEY, roleId);
        redisTemplate.opsForHash().delete(
                RbacAuthConstants.ROLE_AUTH_IDS_KEY, roleId);
    }
}
